{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f331037f-be3f-4782-856f-d55dab952488",
   "metadata": {},
   "source": [
    "# How to migrate chains to LCEL\n",
    "\n",
    ":::info Prerequisites\n",
    "\n",
    "This guide assumes familiarity with the following concepts:\n",
    "- [LangChain Expression Language](/docs/concepts#langchain-expression-language-lcel)\n",
    "\n",
    ":::\n",
    "\n",
    "LCEL is designed to streamline the process of building useful apps with LLMs and combining related components. It does this by providing:\n",
    "\n",
    "1. **A unified interface**: Every LCEL object implements the `Runnable` interface, which defines a common set of invocation methods (`invoke`, `batch`, `stream`, `ainvoke`, ...). This makes it possible to also automatically and consistently support useful operations like streaming of intermediate steps and batching, since every chain composed of LCEL objects is itself an LCEL object.\n",
    "2. **Composition primitives**: LCEL provides a number of primitives that make it easy to compose chains, parallelize components, add fallbacks, dynamically configure chain internals, and more.\n",
    "\n",
    "LangChain maintains a number of legacy abstractions. Many of these can be reimplemented via short combinations of LCEL primitives. Doing so confers some general advantages:\n",
    "\n",
    "- The resulting chains typically implement the full `Runnable` interface, including streaming and asynchronous support where appropriate;\n",
    "- The chains may be more easily extended or modified;\n",
    "- The parameters of the chain are typically surfaced for easier customization (e.g., prompts) over previous versions, which tended to be subclasses and had opaque parameters and internals.\n",
    "\n",
    "The LCEL implementations can be slightly more verbose, but there are significant benefits in transparency and customizability.\n",
    "\n",
    "In this guide we review LCEL implementations of common legacy abstractions. Where appropriate, we link out to separate guides with more detail."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b99b47ec",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install --upgrade --quiet langchain-community langchain langchain-openai faiss-cpu"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "717c8673",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from getpass import getpass\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = getpass()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3621b62-a037-42b8-8faa-59575608bb8b",
   "metadata": {},
   "source": [
    "## `LLMChain`\n",
    "<span data-heading-keywords=\"llmchain\"></span>\n",
    "\n",
    "[`LLMChain`](https://api.python.langchain.com/en/latest/chains/langchain.chains.llm.LLMChain.html) combined a prompt template, LLM, and output parser into a class.\n",
    "\n",
    "Some advantages of switching to the LCEL implementation are:\n",
    "\n",
    "- Clarity around contents and parameters. The legacy `LLMChain` contains a default output parser and other options.\n",
    "- Easier streaming. `LLMChain` only supports streaming via callbacks.\n",
    "- Easier access to raw message outputs if desired. `LLMChain` only exposes these via a parameter or via callback.\n",
    "\n",
    "import { ColumnContainer, Column } from \"@theme/Columns\";\n",
    "\n",
    "<ColumnContainer>\n",
    "\n",
    "<Column>\n",
    "\n",
    "#### Legacy\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e628905c-430e-4e4a-9d7c-c91d2f42052e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'adjective': 'funny',\n",
       " 'text': \"Why couldn't the bicycle find its way home?\\n\\nBecause it lost its bearings!\"}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chains import LLMChain\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [(\"user\", \"Tell me a {adjective} joke\")],\n",
    ")\n",
    "\n",
    "chain = LLMChain(llm=ChatOpenAI(), prompt=prompt)\n",
    "\n",
    "chain({\"adjective\": \"funny\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdc3b527-c09e-4c77-9711-c3cc4506cd95",
   "metadata": {},
   "source": [
    "\n",
    "</Column>\n",
    "\n",
    "<Column>\n",
    "\n",
    "#### LCEL\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0d2a7cf8-1bc7-405c-bb0d-f2ab2ba3b6ab",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Why couldn't the bicycle stand up by itself?\\n\\nBecause it was two tired!\""
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [(\"user\", \"Tell me a {adjective} joke\")],\n",
    ")\n",
    "\n",
    "chain = prompt | ChatOpenAI() | StrOutputParser()\n",
    "\n",
    "chain.invoke({\"adjective\": \"funny\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c0b0513-77b8-4371-a20e-3e487cec7e7f",
   "metadata": {},
   "source": [
    "\n",
    "</Column>\n",
    "</ColumnContainer>\n",
    "\n",
    "Note that `LLMChain` by default returns a `dict` containing both the input and the output. If this behavior is desired, we can replicate it using another LCEL primitive, [`RunnablePassthrough`](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.passthrough.RunnablePassthrough.html):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "529206c5-abbe-4213-9e6c-3b8586c8000d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'adjective': 'funny',\n",
       " 'text': \"Why couldn't the bicycle stand up by itself?\\n\\nBecause it was two tired!\"}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.runnables import RunnablePassthrough\n",
    "\n",
    "outer_chain = RunnablePassthrough().assign(text=chain)\n",
    "\n",
    "outer_chain.invoke({\"adjective\": \"funny\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29d2e26c-2854-4971-9c2b-613450993921",
   "metadata": {},
   "source": [
    "See [this tutorial](/docs/tutorials/llm_chain) for more detail on building with prompt templates, LLMs, and output parsers."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00df631d-5121-4918-94aa-b88acce9b769",
   "metadata": {},
   "source": [
    "## `ConversationChain`\n",
    "<span data-heading-keywords=\"conversationchain\"></span>\n",
    "\n",
    "[`ConversationChain`](https://api.python.langchain.com/en/latest/chains/langchain.chains.conversation.base.ConversationChain.html) incorporates a memory of previous messages to sustain a stateful conversation.\n",
    "\n",
    "Some advantages of switching to the LCEL implementation are:\n",
    "\n",
    "- Innate support for threads/separate sessions. To make this work with `ConversationChain`, you'd need to instantiate a separate memory class outside the chain.\n",
    "- More explicit parameters. `ConversationChain` contains a hidden default prompt, which can cause confusion.\n",
    "- Streaming support. `ConversationChain` only supports streaming via callbacks.\n",
    "\n",
    "`RunnableWithMessageHistory` implements sessions via configuration parameters. It should be instantiated with a callable that returns a [chat message history](https://api.python.langchain.com/en/latest/chat_history/langchain_core.chat_history.BaseChatMessageHistory.html). By default, it expects this function to take a single argument `session_id`.\n",
    "\n",
    "<ColumnContainer>\n",
    "<Column>\n",
    "\n",
    "#### Legacy\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "4f2cc6dc-d70a-4c13-9258-452f14290da6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': 'how are you?',\n",
       " 'history': '',\n",
       " 'response': \"Arrr, I be doin' well, me matey! Just sailin' the high seas in search of treasure and adventure. How can I assist ye today?\"}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chains import ConversationChain\n",
    "from langchain.memory import ConversationBufferMemory\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "template = \"\"\"\n",
    "You are a pirate. Answer the following questions as best you can.\n",
    "Chat history: {history}\n",
    "Question: {input}\n",
    "\"\"\"\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "memory = ConversationBufferMemory()\n",
    "\n",
    "chain = ConversationChain(\n",
    "    llm=ChatOpenAI(),\n",
    "    memory=memory,\n",
    "    prompt=prompt,\n",
    ")\n",
    "\n",
    "chain({\"input\": \"how are you?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8e36b0e-c7dc-4130-a51b-189d4b756c7f",
   "metadata": {},
   "source": [
    "</Column>\n",
    "\n",
    "<Column>\n",
    "\n",
    "#### LCEL\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "173e1a9c-2a18-4669-b0de-136f39197786",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Arr, matey! I be sailin' the high seas with me crew, searchin' for buried treasure and adventure! How be ye doin' on this fine day?\""
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.chat_history import InMemoryChatMessageHistory\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"You are a pirate. Answer the following questions as best you can.\"),\n",
    "        (\"placeholder\", \"{chat_history}\"),\n",
    "        (\"human\", \"{input}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "history = InMemoryChatMessageHistory()\n",
    "\n",
    "chain = prompt | ChatOpenAI() | StrOutputParser()\n",
    "\n",
    "wrapped_chain = RunnableWithMessageHistory(chain, lambda x: history)\n",
    "\n",
    "wrapped_chain.invoke(\n",
    "    {\"input\": \"how are you?\"},\n",
    "    config={\"configurable\": {\"session_id\": \"42\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b386ce6-895e-442c-88f3-7bec0ab9f401",
   "metadata": {},
   "source": [
    "\n",
    "</Column>\n",
    "</ColumnContainer>\n",
    "\n",
    "The above example uses the same `history` for all sessions. The example below shows how to use a different chat history for each session."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4e05994f-1fbc-4699-bf2e-62cb0e4deeb8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"Ahoy there! What be ye wantin' from this old pirate?\", response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 29, 'total_tokens': 44}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-1846d5f5-0dda-43b6-bb49-864e541f9c29-0', usage_metadata={'input_tokens': 29, 'output_tokens': 15, 'total_tokens': 44})"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.chat_history import BaseChatMessageHistory\n",
    "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
    "\n",
    "store = {}\n",
    "\n",
    "\n",
    "def get_session_history(session_id: str) -> BaseChatMessageHistory:\n",
    "    if session_id not in store:\n",
    "        store[session_id] = InMemoryChatMessageHistory()\n",
    "    return store[session_id]\n",
    "\n",
    "\n",
    "chain = prompt | ChatOpenAI() | StrOutputParser()\n",
    "\n",
    "wrapped_chain = RunnableWithMessageHistory(chain, get_session_history)\n",
    "\n",
    "wrapped_chain.invoke(\"Hello!\", config={\"configurable\": {\"session_id\": \"abc123\"}})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c36ebecb",
   "metadata": {},
   "source": [
    "See [this tutorial](/docs/tutorials/chatbot) for a more end-to-end guide on building with [`RunnableWithMessageHistory`](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html).\n",
    "\n",
    "## `RetrievalQA`\n",
    "<span data-heading-keywords=\"retrievalqa\"></span>\n",
    "\n",
    "The [`RetrievalQA`](https://api.python.langchain.com/en/latest/chains/langchain.chains.retrieval_qa.base.RetrievalQA.html) chain performed natural-language question answering over a data source using retrieval-augmented generation.\n",
    "\n",
    "Some advantages of switching to the LCEL implementation are:\n",
    "\n",
    "- Easier customizability. Details such as the prompt and how documents are formatted are only configurable via specific parameters in the `RetrievalQA` chain.\n",
    "- More easily return source documents.\n",
    "- Support for runnable methods like streaming and async operations.\n",
    "\n",
    "Now let's look at them side-by-side. We'll use the same ingestion code to load a [blog post by Lilian Weng](https://lilianweng.github.io/posts/2023-06-23-agent/) on autonomous agents into a local vector store:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "1efbe16e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load docs\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_openai.chat_models import ChatOpenAI\n",
    "from langchain_openai.embeddings import OpenAIEmbeddings\n",
    "\n",
    "loader = WebBaseLoader(\"https://lilianweng.github.io/posts/2023-06-23-agent/\")\n",
    "data = loader.load()\n",
    "\n",
    "# Split\n",
    "text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)\n",
    "all_splits = text_splitter.split_documents(data)\n",
    "\n",
    "# Store splits\n",
    "vectorstore = FAISS.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())\n",
    "\n",
    "# LLM\n",
    "llm = ChatOpenAI()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7e16438",
   "metadata": {},
   "source": [
    "<ColumnContainer>\n",
    "\n",
    "<Column>\n",
    "\n",
    "#### Legacy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "43bf55a0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'query': 'What are autonomous agents?',\n",
       " 'result': 'Autonomous agents are LLM-empowered agents that handle autonomous design, planning, and performance of complex tasks, such as scientific experiments. These agents can browse the Internet, read documentation, execute code, call robotics experimentation APIs, and leverage other LLMs. They are capable of reasoning and planning ahead for complicated tasks by breaking them down into smaller steps.'}"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain import hub\n",
    "from langchain.chains import RetrievalQA\n",
    "\n",
    "# See full prompt at https://smith.langchain.com/hub/rlm/rag-prompt\n",
    "prompt = hub.pull(\"rlm/rag-prompt\")\n",
    "\n",
    "qa_chain = RetrievalQA.from_llm(\n",
    "    llm, retriever=vectorstore.as_retriever(), prompt=prompt\n",
    ")\n",
    "\n",
    "qa_chain(\"What are autonomous agents?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "081948e5",
   "metadata": {},
   "source": [
    "</Column>\n",
    "\n",
    "<Column>\n",
    "\n",
    "#### LCEL\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "9efcc931",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Autonomous agents are agents that can handle autonomous design, planning, and performance of complex tasks, such as scientific experiments. They can browse the Internet, read documentation, execute code, call robotics experimentation APIs, and leverage other language model models. These agents use reasoning steps to develop solutions to specific tasks, like creating a novel anticancer drug.'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain import hub\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "\n",
    "# See full prompt at https://smith.langchain.com/hub/rlm/rag-prompt\n",
    "prompt = hub.pull(\"rlm/rag-prompt\")\n",
    "\n",
    "\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
    "\n",
    "\n",
    "qa_chain = (\n",
    "    {\n",
    "        \"context\": vectorstore.as_retriever() | format_docs,\n",
    "        \"question\": RunnablePassthrough(),\n",
    "    }\n",
    "    | prompt\n",
    "    | llm\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "qa_chain.invoke(\"What are autonomous agents?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6f44fe8",
   "metadata": {},
   "source": [
    "</Column>\n",
    "</ColumnContainer>\n",
    "\n",
    "The LCEL implementation exposes the internals of what's happening around retrieving, formatting documents, and passing them through a prompt to the LLM, but it is more verbose. You can customize and wrap this composition logic in a helper function, or use the higher-level [`create_retrieval_chain`](https://api.python.langchain.com/en/latest/chains/langchain.chains.retrieval.create_retrieval_chain.html) and [`create_stuff_documents_chain`](https://api.python.langchain.com/en/latest/chains/langchain.chains.combine_documents.stuff.create_stuff_documents_chain.html) helper method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "5fe42761",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': 'What are autonomous agents?',\n",
       " 'context': [Document(page_content='Boiko et al. (2023) also looked into LLM-empowered agents for scientific discovery, to handle autonomous design, planning, and performance of complex scientific experiments. This agent can use tools to browse the Internet, read documentation, execute code, call robotics experimentation APIs and leverage other LLMs.\\nFor example, when requested to \"develop a novel anticancer drug\", the model came up with the following reasoning steps:', metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'}),\n",
       "  Document(page_content='Weng, Lilian. (Jun 2023). “LLM-powered Autonomous Agents”. Lil’Log. https://lilianweng.github.io/posts/2023-06-23-agent/.', metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'}),\n",
       "  Document(page_content='Fig. 1. Overview of a LLM-powered autonomous agent system.\\nComponent One: Planning#\\nA complicated task usually involves many steps. An agent needs to know what they are and plan ahead.\\nTask Decomposition#', metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'}),\n",
       "  Document(page_content=\"LLM Powered Autonomous Agents | Lil'Log\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nLil'Log\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nPosts\\n\\n\\n\\n\\nArchive\\n\\n\\n\\n\\nSearch\\n\\n\\n\\n\\nTags\\n\\n\\n\\n\\nFAQ\\n\\n\\n\\n\\nemojisearch.app\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n      LLM Powered Autonomous Agents\\n    \\nDate: June 23, 2023  |  Estimated Reading Time: 31 min  |  Author: Lilian Weng\\n\\n\\n \\n\\n\\nTable of Contents\\n\\n\\n\\nAgent System Overview\\n\\nComponent One: Planning\\n\\nTask Decomposition\\n\\nSelf-Reflection\\n\\n\\nComponent Two: Memory\\n\\nTypes of Memory\\n\\nMaximum Inner Product Search (MIPS)\", metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'})],\n",
       " 'answer': 'Autonomous agents are entities that can operate independently, making decisions and taking actions without direct human intervention. These agents can perform tasks such as planning, executing complex experiments, and leveraging various tools and resources to achieve objectives. In the context provided, LLM-powered autonomous agents are specifically designed for scientific discovery, capable of handling tasks like designing novel anticancer drugs through reasoning steps.'}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain import hub\n",
    "from langchain.chains import create_retrieval_chain\n",
    "from langchain.chains.combine_documents import create_stuff_documents_chain\n",
    "\n",
    "# See full prompt at https://smith.langchain.com/hub/langchain-ai/retrieval-qa-chat\n",
    "retrieval_qa_chat_prompt = hub.pull(\"langchain-ai/retrieval-qa-chat\")\n",
    "\n",
    "combine_docs_chain = create_stuff_documents_chain(llm, retrieval_qa_chat_prompt)\n",
    "rag_chain = create_retrieval_chain(vectorstore.as_retriever(), combine_docs_chain)\n",
    "\n",
    "rag_chain.invoke({\"input\": \"What are autonomous agents?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2772f4e9",
   "metadata": {},
   "source": [
    "## `ConversationalRetrievalChain`\n",
    "<span data-heading-keywords=\"conversationalretrievalchain\"></span>\n",
    "\n",
    "The [`ConversationalRetrievalChain`](https://api.python.langchain.com/en/latest/chains/langchain.chains.conversational_retrieval.base.ConversationalRetrievalChain.html) was an all-in one way that combined retrieval-augmented generation with chat history, allowing you to \"chat with\" your documents.\n",
    "\n",
    "Advantages of switching to the LCEL implementation are similar to the `RetrievalQA` section above:\n",
    "\n",
    "- Clearer internals. The `ConversationalRetrievalChain` chain hides an entire question rephrasing step which dereferences the initial query against the chat history.\n",
    "  - This means the class contains two sets of configurable prompts, LLMs, etc.\n",
    "- More easily return source documents.\n",
    "- Support for runnable methods like streaming and async operations.\n",
    "\n",
    "Here are side-by-side implementations with custom prompts. We'll reuse the loaded documents and vector store from the previous section:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8bc06416",
   "metadata": {},
   "source": [
    "<ColumnContainer>\n",
    "\n",
    "<Column>\n",
    "\n",
    "#### Legacy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "54eb9576",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'question': 'What are autonomous agents?',\n",
       " 'chat_history': '',\n",
       " 'answer': 'Autonomous agents are powered by Large Language Models (LLMs) to handle tasks like scientific discovery and complex experiments autonomously. These agents can browse the internet, read documentation, execute code, and leverage other LLMs to perform tasks. They can reason and plan ahead to decompose complicated tasks into manageable steps.'}"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chains import ConversationalRetrievalChain\n",
    "\n",
    "condense_question_template = \"\"\"\n",
    "Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.\n",
    "\n",
    "Chat History:\n",
    "{chat_history}\n",
    "Follow Up Input: {question}\n",
    "Standalone question:\"\"\"\n",
    "\n",
    "condense_question_prompt = ChatPromptTemplate.from_template(condense_question_template)\n",
    "\n",
    "qa_template = \"\"\"\n",
    "You are an assistant for question-answering tasks.\n",
    "Use the following pieces of retrieved context to answer\n",
    "the question. If you don't know the answer, say that you\n",
    "don't know. Use three sentences maximum and keep the\n",
    "answer concise.\n",
    "\n",
    "Chat History:\n",
    "{chat_history}\n",
    "\n",
    "Other context:\n",
    "{context}\n",
    "\n",
    "Question: {question}\n",
    "\"\"\"\n",
    "\n",
    "qa_prompt = ChatPromptTemplate.from_template(qa_template)\n",
    "\n",
    "convo_qa_chain = ConversationalRetrievalChain.from_llm(\n",
    "    llm,\n",
    "    vectorstore.as_retriever(),\n",
    "    condense_question_prompt=condense_question_prompt,\n",
    "    combine_docs_chain_kwargs={\n",
    "        \"prompt\": qa_prompt,\n",
    "    },\n",
    ")\n",
    "\n",
    "convo_qa_chain(\n",
    "    {\n",
    "        \"question\": \"What are autonomous agents?\",\n",
    "        \"chat_history\": \"\",\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43a8a23c",
   "metadata": {},
   "source": [
    "</Column>\n",
    "\n",
    "<Column>\n",
    "\n",
    "#### LCEL\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "c884b138",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': 'What are autonomous agents?',\n",
       " 'chat_history': [],\n",
       " 'context': [Document(page_content='Boiko et al. (2023) also looked into LLM-empowered agents for scientific discovery, to handle autonomous design, planning, and performance of complex scientific experiments. This agent can use tools to browse the Internet, read documentation, execute code, call robotics experimentation APIs and leverage other LLMs.\\nFor example, when requested to \"develop a novel anticancer drug\", the model came up with the following reasoning steps:', metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'}),\n",
       "  Document(page_content='Weng, Lilian. (Jun 2023). “LLM-powered Autonomous Agents”. Lil’Log. https://lilianweng.github.io/posts/2023-06-23-agent/.', metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'}),\n",
       "  Document(page_content='Fig. 1. Overview of a LLM-powered autonomous agent system.\\nComponent One: Planning#\\nA complicated task usually involves many steps. An agent needs to know what they are and plan ahead.\\nTask Decomposition#', metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'}),\n",
       "  Document(page_content='Or\\n@article{weng2023agent,\\n  title   = \"LLM-powered Autonomous Agents\",\\n  author  = \"Weng, Lilian\",\\n  journal = \"lilianweng.github.io\",\\n  year    = \"2023\",\\n  month   = \"Jun\",\\n  url     = \"https://lilianweng.github.io/posts/2023-06-23-agent/\"\\n}\\nReferences#\\n[1] Wei et al. “Chain of thought prompting elicits reasoning in large language models.” NeurIPS 2022\\n[2] Yao et al. “Tree of Thoughts: Dliberate Problem Solving with Large Language Models.” arXiv preprint arXiv:2305.10601 (2023).', metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'title': \"LLM Powered Autonomous Agents | Lil'Log\", 'description': 'Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.\\nAgent System Overview In a LLM-powered autonomous agent system, LLM functions as the agent’s brain, complemented by several key components:', 'language': 'en'})],\n",
       " 'answer': 'Autonomous agents are entities capable of acting independently, making decisions, and performing tasks without direct human intervention. These agents can interact with their environment, perceive information, and take actions based on their goals or objectives. They often use artificial intelligence techniques to navigate and accomplish tasks in complex or dynamic environments.'}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chains import create_history_aware_retriever, create_retrieval_chain\n",
    "\n",
    "condense_question_system_template = (\n",
    "    \"Given a chat history and the latest user question \"\n",
    "    \"which might reference context in the chat history, \"\n",
    "    \"formulate a standalone question which can be understood \"\n",
    "    \"without the chat history. Do NOT answer the question, \"\n",
    "    \"just reformulate it if needed and otherwise return it as is.\"\n",
    ")\n",
    "\n",
    "condense_question_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", condense_question_system_template),\n",
    "        (\"placeholder\", \"{chat_history}\"),\n",
    "        (\"human\", \"{input}\"),\n",
    "    ]\n",
    ")\n",
    "history_aware_retriever = create_history_aware_retriever(\n",
    "    llm, vectorstore.as_retriever(), condense_question_prompt\n",
    ")\n",
    "\n",
    "system_prompt = (\n",
    "    \"You are an assistant for question-answering tasks. \"\n",
    "    \"Use the following pieces of retrieved context to answer \"\n",
    "    \"the question. If you don't know the answer, say that you \"\n",
    "    \"don't know. Use three sentences maximum and keep the \"\n",
    "    \"answer concise.\"\n",
    "    \"\\n\\n\"\n",
    "    \"{context}\"\n",
    ")\n",
    "\n",
    "qa_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system_prompt),\n",
    "        (\"placeholder\", \"{chat_history}\"),\n",
    "        (\"human\", \"{input}\"),\n",
    "    ]\n",
    ")\n",
    "qa_chain = create_stuff_documents_chain(llm, qa_prompt)\n",
    "\n",
    "convo_qa_chain = create_retrieval_chain(history_aware_retriever, qa_chain)\n",
    "\n",
    "convo_qa_chain.invoke(\n",
    "    {\n",
    "        \"input\": \"What are autonomous agents?\",\n",
    "        \"chat_history\": [],\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2717810",
   "metadata": {},
   "source": [
    "</Column>\n",
    "\n",
    "</ColumnContainer>\n",
    "\n",
    "## Next steps\n",
    "\n",
    "You've now seen how to migrate existing usage of some legacy chains to LCEL.\n",
    "\n",
    "Next, check out the [LCEL conceptual docs](/docs/concepts/#langchain-expression-language-lcel) for more background information."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
